//
// Created by denglibin on 2021/4/2 0002.
//
#include "gl_object.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

GLuint createVBO(float vertices[], int arrLen){
    GLuint VBO;
    glGenBuffers(1, &VBO);
    /*
    OpenGL有很多缓冲对象类型，顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER。OpenGL允许我们同时绑定多个缓冲，只要它们是不同的缓冲类型。我们可以使用glBindBuffer函数把新创建的缓冲绑定到GL_ARRAY_BUFFER目标上：
    */
    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    /*
    传递数据到缓冲区:从这一刻起，我们使用的任何（在GL_ARRAY_BUFFER目标上的）缓冲调用都会用来配置当前绑定的缓冲(VBO)。然后我们可以调用glBufferData函数，它会把之前定义的顶点数据复制到缓冲的内存中
    glBufferData是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数。它的第一个参数是目标缓冲的类型：顶点缓冲对象当前绑定到GL_ARRAY_BUFFER目标上。第二个参数指定传输数据的大小(以字节为单位)；用一个简单的sizeof计算出顶点数据大小就行。第三个参数是我们希望发送的实际数据。
第四个参数指定了我们希望显卡如何管理给定的数据。它有三种形式：
GL_STATIC_DRAW ：数据不会或几乎不会改变。
GL_DYNAMIC_DRAW：数据会被改变很多。
GL_STREAM_DRAW ：数据每次绘制时都会改变。
三角形的位置数据不会改变，每次渲染调用时都保持原样，所以它的使用类型最好是GL_STATIC_DRAW。如果，比如说一个缓冲中的数据将频繁被改变，那么使用的类型就是GL_DYNAMIC_DRAW或GL_STREAM_DRAW，这样就能确保显卡把数据放在能够高速写入的内存部分。
    */
    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * arrLen , vertices, GL_STATIC_DRAW);
    //现在我们已经把顶点数据储存在显卡的内存中，用VBO这个顶点缓冲对象管理。
    return VBO;
}

 GLuint createVAO(){
    GLuint VAO;
    glGenVertexArrays(1, &VAO);
    //绑定VAO
    glBindVertexArray(VAO);
    return VAO;
}
/**
 * 创建索引缓冲对象 EBO
 * @return
 */
 GLuint createEBO(unsigned int indices[], int arrLen){
    unsigned int EBO;
    glGenBuffers(1, &EBO);
    //绑定EBO
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    //传递索引数据到缓冲区
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(float) * arrLen, indices, GL_STATIC_DRAW);
    return EBO;
}
/**
 * 创建纹理对象
 * @param imgPath
 * @return
 */
GLuint createTexture(const char* imgPath, int format, int glFormat){
    unsigned int texture;
    glGenTextures(1, &texture);
    //绑定它,让之后任何的纹理指令都可以配置当前绑定的纹理
    glBindTexture(GL_TEXTURE_2D, texture);
    // 为当前绑定的纹理对象设置环绕、过滤方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    //OpenGL要求y轴0.0坐标是在图片的底部的，但是图片的y轴0.0坐标通常在顶部。很幸运，stb_image.h能够在图像加载时帮助我们翻转y轴
    stbi_set_flip_vertically_on_load(1);
    int width, height, nrChannels;// 宽度、高度和颜色通道
    //加载图片
    unsigned char *data = stbi_load(imgPath, &width, &height, &nrChannels, 0);
    if(!data){
        printf("Failed to load texture\n");
        exit(1);
    }
    //使用载入的图片数据生成一个纹理。纹理可以通过glTexImage2D来生成
    /**
第一个参数指定了纹理目标(Target)。设置为GL_TEXTURE_2D意味着会生成与当前绑定的纹理对象在同一个目标上的纹理（任何绑定到GL_TEXTURE_1D和GL_TEXTURE_3D的纹理不会受到影响）。
第二个参数为纹理指定多级渐远纹理的级别，如果你希望单独手动设置每个多级渐远纹理的级别的话。这里我们填0，也就是基本级别。
第三个参数告诉OpenGL我们希望把纹理储存为何种格式。我们的图像只有RGB值，因此我们也把纹理储存为RGB值。
第四个和第五个参数设置最终的纹理的宽度和高度。我们之前加载图像的时候储存了它们，所以我们使用对应的变量。
下个参数应该总是被设为0（历史遗留的问题）。
第七第八个参数定义了源图的格式和数据类型。我们使用RGB值加载这个图像，并把它们储存为char(byte)数组，我们将会传入对应值。
最后一个参数是真正的图像数据。

     当调用glTexImage2D时，当前绑定的纹理对象就会被附加上纹理图像。然而，目前只有基本级别(Base-level)的纹理图像被加载了，如果要使用多级渐远纹理，
     我们必须手动设置所有不同的图像（不断递增第二个参数）。或者，直接在生成纹理之后调用glGenerateMipmap。这会为当前绑定的纹理自动生成所有需要的多级渐远纹理
     */
    glTexImage2D(GL_TEXTURE_2D, 0, glFormat, width, height, 0, format, GL_UNSIGNED_BYTE, data);
    glGenerateMipmap(GL_TEXTURE_2D);

    //释放图像的内存
    stbi_image_free(data);
    return texture;
}